# _*_ coding: utf-8 _*_
# .@FileName:gen_name_exp
# .@Data....:2025-01-31 : 11 : 33
# .@Aurhor..:LiuJingYu
"""
launch:
   import maya.cmds as cmds
   FileName.main() 
"""
# from __future__ import unicode_literals, print_function
# import re
#
# class TokenBase(object):
#     def __init__(self, s):
#         self.s = s
#     def __repr__(self):
#         return "{}<{}>".format(self.__class__.__name__, self.s)
#
# class GenNameExpExc(Exception):
#     pass
#
# class NameToken(TokenBase):
#     pass
#
# class ValueNameToken(TokenBase):
#     pass
#
# skip_space = re.compile(r"[ \t]+")
# name_match = re.compile(r"[a-zA-Z0-9_]+")
# value_name_match = re.compile(r"\{[a-zA-Z0-9_]+\}")
#
# def _lex(exp):
#     exp = exp
#     while True:
#         m = skip_space.match(exp)
#
#         if m:
#             exp = exp[m.end():]
#
#         if len(exp) == 0:
#             return
#
#         m = name_match.match(exp)
#
#         if m:
#             token = NameToken(exp[m.start(): m.end()])
#             exp = exp[m.end():]
#             yield token
#             continue
#
#         m = value_name_match.match(exp)
#
#         if m:
#             token = ValueNameToken(exp[m.start()+1: m.end()-1])
#             exp = exp[m.end():]
#             yield token
#             continue
#
#         raise GenNameExpExc("lex error")
#
#
# def compile(exp, values):
#
#     tokens = list(_lex(exp))
#     for kv in values:
#         name = ""
#         for t in tokens:
#             if isinstance(t, NameToken):
#                 name += t.s
#             else:
#                 v = kv.get(t.s)
#                 if v is None:
#                     raise GenNameExpExc("ket not found")
#                 name += str(v)
#         yield name
#
# if __name__ == "__main__":
#     test_exp = "bix_{name_id}_joint"
#
#     print(list(_lex(test_exp)))
#
#     print(list(compile(test_exp,
#                        [
#                            {"name_id": 0},
#                            {"name_id": 1},
#                            {"name_id": 2}
#                        ])))


# -*- coding: utf-8 -*-
"""
名称生成器模块
功能：解析包含占位符的字符串模板，根据输入值生成具体名称

示例：
>>> compile("obj_{part}_v{version}", [{"part": "arm", "version": 1}])
["obj_arm_v1"]
"""

import re
from typing import Dict, Iterable, List, Union


class Token:
    """词法单元基类"""

    def __init__(self, value: str):
        """
        初始化词法单元
        :param value: 原始字符串值
        """
        self.value = value

    def __repr__(self) -> str:
        """调试用字符串表示"""
        return f"{self.__class__.__name__}<{self.value}>"


class NameToken(Token):
    """普通名称词法单元（匹配字母、数字、下划线组合）"""


class VariableToken(Token):
    """变量占位符词法单元（匹配 {variable} 格式）"""


class TemplateError(Exception):
    """模板处理异常基类"""


class CompilationError(TemplateError):
    """编译过程异常"""


# 预编译正则表达式（提升性能）
_SPACE_PATTERN = re.compile(r"[ \t]+")  # 空格/tab
_NAME_PATTERN = re.compile(r"[a-zA-Z0-9_]+")  # 普通名称
_VAR_PATTERN = re.compile(r"\{([a-zA-Z0-9_]+)\}")  # 变量占位符


def tokenize(template: str) -> List[Token]:
    """
    将模板字符串转换为词法单元列表
    :param template: 输入模板字符串
    :return: 词法单元列表
    :raises TemplateError: 遇到无法识别的字符时抛出
    """
    tokens = []
    remaining = template

    while remaining:
        # 跳过空白字符
        space_match = _SPACE_PATTERN.match(remaining)
        if space_match:
            remaining = remaining[space_match.end():]
            continue

        # 尝试匹配变量占位符
        var_match = _VAR_PATTERN.match(remaining)
        if var_match:
            var_name = var_match.group(1)
            tokens.append(VariableToken(var_name))
            remaining = remaining[var_match.end():]
            continue

        # 尝试匹配普通名称
        name_match = _NAME_PATTERN.match(remaining)
        if name_match:
            name_str = name_match.group()
            tokens.append(NameToken(name_str))
            remaining = remaining[name_match.end():]
            continue

        # 错误处理：显示前20个字符作为上下文
        error_context = remaining[:20].replace("\n", "\\n")
        raise TemplateError(f"无法识别的字符: '{error_context}...'")

    return tokens


def compile_template(
        template: str,
        values: Iterable[Dict[str, Union[str, int]]]
) -> List[str]:
    """
    编译模板生成名称列表
    :param template: 模板字符串
    :param values: 值字典列表
    :return: 生成的结果列表
    :raises CompilationError: 键不存在或处理失败时抛出
    """
    try:
        tokens = tokenize(template)
    except TemplateError as e:
        raise CompilationError(f"模板解析失败: {str(e)}") from e

    results = []

    for value_set in values:
        try:
            parts = []
            for token in tokens:
                if isinstance(token, NameToken):
                    parts.append(token.value)
                elif isinstance(token, VariableToken):
                    # 检查键是否存在
                    if token.value not in value_set:
                        raise CompilationError(f"缺少必要键: {token.value}")
                    parts.append(str(value_set[token.value]))
            results.append("".join(parts))
        except CompilationError as e:
            raise CompilationError(
                f"处理值集 {value_set} 时失败: {str(e)}"
            ) from e

    return results


if __name__ == "__main__":
    # 测试用例
    test_template = "bix_{name_id}_joint"

    # 测试词法分析
    try:
        print("词法分析结果:", tokenize(test_template))
    except TemplateError as e:
        print(f"词法分析错误: {str(e)}")

    # 测试模板编译
    try:
        test_values = [{"name_id": num} for num in range(3)]
        print("编译结果:", compile_template(test_template, test_values))
    except CompilationError as e:
        print(f"编译错误: {str(e)}")